﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using Tools.Debugging;

namespace repliback4.Misc.Queue {
    public class AutoQueue<T> :IDisposable{
        public static int workerno = 0;
        protected Queue<T> queue = new Queue<T>( );
        public delegate void OnItemInQueueHandler ( T queueItem );
        public event OnItemInQueueHandler OnItemInQueue;
        protected Boolean doDequeue = false;
        protected Thread dequeuingThread;
        public Boolean Dequeuing {
            set { if ( value ) { this.hold.Set( ); } else { this.hold.Reset( ); } this.doDequeue = value; }
            get { return this.doDequeue; }
        }
        protected ManualResetEvent hold = new ManualResetEvent( true );
        protected AutoResetEvent wh = new AutoResetEvent( false );
        protected void CallOnItemInQueueHandler ( T queueItem ) {
            Logger.Debug( this, "--------------  Calling onDequeue {0}", queueItem );

            if ( this.OnItemInQueue != null ) this.OnItemInQueue( queueItem );
            //Logger.DebugFormat( this, "Called onDequeue {0}", typeof( T ).Name );
        }

        protected void DequeueWorker ( ) {
            Thread.CurrentThread.Name = "DequeueWorker" + ( ++AutoQueue<T>.workerno );
            Logger.Debug( this, "DequeueWorker started and named" );

            do {
                if ( this.OnItemInQueue == null ) {
                    this.Dequeuing = false;
                }
                Logger.Debug( this, "waiting on hold {0}", typeof( T ).Name );
                this.hold.WaitOne( );
                Logger.Debug( this, "unholded {0}", typeof( T ).Name );
                this.CallOnItemInQueueHandler( this.Dequeue( ) );
                Logger.Debug( this, "dequeued {0}", typeof( T ).Name );
            } while ( true );


            Logger.Debug( this, "DequeueWorker finished" );

        }

        protected void StartDequeue ( ) {
            Logger.Debug( this, "starting dequeue" );
            this.dequeuingThread = new Thread( new ThreadStart( this.DequeueWorker ) );
            this.dequeuingThread.IsBackground = true;
            //this.dequeuingThread.Name = "dequeuingThread";
            this.dequeuingThread.Start( );
        }

        public AutoQueue ( ) {
            this.StartDequeue( );
        }

        public void Enqueue ( T qi ) {
            lock ( this.queue ) {
                this.queue.Enqueue( qi );
                Logger.Debug( this, "Enequeuing {0}", qi.ToString( ) );
                this.wh.Set( );
            }
        }
        public T Dequeue ( ) {
            this.wh.WaitOne( );
            lock ( this.queue ) {
                T ret = this.queue.Dequeue( );
                if ( this.queue.Count > 0 ) {
                    wh.Set( );
                }
                Logger.Debug( this, "Dequeuing {0}", ret.ToString( ) );
                return ret;
            }
        }

        public int Count {
            get { return this.queue.Count; }
        }

        public void Dispose ( ) {
            this.dequeuingThread.Abort( );
            
        }
    }

    public struct QueueManagerSettings {
        public bool CreateNotExistingQueues;
        public static QueueManagerSettings Default {
            get {
                return new QueueManagerSettings( ) {
                    CreateNotExistingQueues = true
                };
            }
        }
    }
    public class QueueManager<QUEUEITEM, KEY> : IDisposable {
        protected Dictionary<KEY, AutoQueue<QUEUEITEM>> namedQueues = new Dictionary<KEY, AutoQueue<QUEUEITEM>>( );
        //protected Hashtable namedQueues = new Hashtable( );

        protected QueueManagerSettings settings;

        public void CreateQueue ( KEY name ) {
            if ( namedQueues.ContainsKey( name ) ) {
                throw new Exception( String.Format( "queue named '{0}' already exists" ) );
            }
            this.namedQueues.Add( name, new AutoQueue<QUEUEITEM>( ) );
        }

        public QueueManager ( ) {
            Logger.Created( this );
            this.settings = QueueManagerSettings.Default;
        }
        public QueueManager ( QueueManagerSettings settings ) {
            Logger.Created( this );
            this.settings = settings;
        }

        public void Enque ( KEY name, QUEUEITEM item ) {
            this[name].Enqueue( item );
        }
        public QUEUEITEM Deque ( KEY name ) {
            return this[name].Dequeue( );
        }

        public AutoQueue<QUEUEITEM> this[KEY name] {
            get {
                //Logger.Debug( this, "getting queue" );
                AutoQueue<QUEUEITEM> q = this.namedQueues[name];
                //Logger.Debug( this, "got queue hash {0}", q.GetHashCode( ) );
                return q;
            }
        }

        public void AddOnItemInQueueHandler ( KEY name, AutoQueue<QUEUEITEM>.OnItemInQueueHandler handler ) {
            this[name].OnItemInQueue += handler;
        }

        public bool QueueExists ( KEY name ) {
            return this.namedQueues.ContainsKey( name );
        }

        public int Count {
            get { return this.namedQueues.Count; }
        }
        public int CountInQueue ( KEY name ) {
            return this[name].Count;
        }

        #region IDisposable Members

        public void Dispose ( ) {
            foreach ( KeyValuePair<KEY,AutoQueue<QUEUEITEM>> aq in this.namedQueues ) {
                aq.Value.Dispose( );
            }
        }

        #endregion
    }
}
